/**********************************************************************

Audacity: A Digital Audio Editor

SampleBlock.h

**********************************************************************/

#ifndef __AUDACITY_SAMPLE_BLOCK__
#define __AUDACITY_SAMPLE_BLOCK__

#include "GlobalVariable.h"
#include "SampleFormat.h"
#include "AudioSegmentSampleView.h"

#include <functional>
#include <memory>
#include <unordered_set>

#include "Observer.h"
#include "XMLTagHandler.h"

class AudacityProject;
class ProjectFileIO;
class XMLWriter;

class SampleBlock;
using SampleBlockPtr = std::shared_ptr<SampleBlock>;
using SampleBlockConstPtr = std::shared_ptr<const SampleBlock>;
class SampleBlockFactory;
using SampleBlockFactoryPtr = std::shared_ptr<SampleBlockFactory>;

using SampleBlockID = long long;

class MinMaxRMS
{
public:
    float min = 0;
    float max = 0;
    float RMS = 0;
};

///\brief Abstract class allows access to contents of a block of sound samples,
/// serialization as XML, and reference count management that can suppress
/// reclamation of its storage
class WAVE_TRACK_API SampleBlock
{
public:
    virtual ~SampleBlock();

    virtual void CloseLock() noexcept = 0;

    virtual SampleBlockID GetBlockID() const = 0;

    // If !mayThrow and there is an error, ignores it and returns zero.
    // That may be appropriate when only attempting to display samples, not edit.
    size_t GetSamples(samplePtr dest, sampleFormat destformat, size_t sampleoffset, size_t numsamples, bool mayThrow = true);

    virtual BlockSampleView GetFloatSampleView(bool mayThrow) = 0;

    virtual sampleFormat GetSampleFormat() const = 0;

    virtual size_t GetSampleCount() const = 0;

    //! Non-throwing, should fill with zeroes on failure
    virtual bool
    GetSummary256(float* dest, size_t frameoffset, size_t numframes) = 0;
    //! Non-throwing, should fill with zeroes on failure
    virtual bool
    GetSummary64k(float* dest, size_t frameoffset, size_t numframes) = 0;

    /// Gets extreme values for the specified region
    // If !mayThrow and there is an error, ignores it and returns zeroes.
    // That may be appropriate when only attempting to display samples, not edit.
    MinMaxRMS GetMinMaxRMS(
        size_t start, size_t len, bool mayThrow = true);

    /// Gets extreme values for the entire block
    // If !mayThrow and there is an error, ignores it and returns zeroes.
    // That may be appropriate when only attempting to display samples, not edit.
    MinMaxRMS GetMinMaxRMS(bool mayThrow = true) const;

    virtual size_t GetSpaceUsage() const = 0;

    virtual void SaveXML(XMLWriter& xmlFile) = 0;

protected:
    virtual size_t DoGetSamples(samplePtr dest, sampleFormat destformat, size_t sampleoffset, size_t numsamples) = 0;

    virtual MinMaxRMS DoGetMinMaxRMS(size_t start, size_t len) = 0;

    virtual MinMaxRMS DoGetMinMaxRMS() const = 0;
};

// Makes a useful function object
inline std::function< void(SampleBlockConstPtr) >
BlockSpaceUsageAccumulator(unsigned long long& total)
{
    return [&total](SampleBlockConstPtr pBlock){
        total += pBlock->GetSpaceUsage();
    };
}

struct SampleBlockCreateMessage { };

///\brief abstract base class with methods to produce @ref SampleBlock objects
class WAVE_TRACK_API SampleBlockFactory : public Observer::Publisher<SampleBlockCreateMessage>
{
public:
    //! Global factory of per-project factories of sample blocks
    struct WAVE_TRACK_API Factory : GlobalHook<Factory,
                                               SampleBlockFactoryPtr(AudacityProject&)
                                               > {};

    // Invoke the installed factory (throw an exception if none was installed)
    static SampleBlockFactoryPtr New(AudacityProject& project);

    virtual ~SampleBlockFactory();

    // Returns a non-null pointer or else throws an exception
    SampleBlockPtr Create(constSamplePtr src, size_t numsamples, sampleFormat srcformat);

    // Returns a non-null pointer or else throws an exception
    SampleBlockPtr CreateSilent(
        size_t numsamples, sampleFormat srcformat);

    // Returns a non-null pointer or else throws an exception
    SampleBlockPtr CreateFromXML(
        sampleFormat srcformat, const AttributesList& attrs);

    // Potentially returns a null pointer
    SampleBlockPtr CreateFromId(sampleFormat srcformat, SampleBlockID id);

    using SampleBlockIDs = std::unordered_set<SampleBlockID>;
    /*! @return ids of all sample blocks created by this factory and still extant */
    virtual SampleBlockIDs GetActiveBlockIDs() = 0;

protected:
    // The override should throw more informative exceptions on error than the
    // default InconsistencyException thrown by Create
    virtual SampleBlockPtr DoCreate(constSamplePtr src, size_t numsamples, sampleFormat srcformat) = 0;

    // The override should throw more informative exceptions on error than the
    // default InconsistencyException thrown by CreateSilent
    virtual SampleBlockPtr DoCreateSilent(
        size_t numsamples, sampleFormat srcformat) = 0;

    // The override should throw more informative exceptions on error than the
    // default InconsistencyException thrown by CreateFromXML
    virtual SampleBlockPtr DoCreateFromXML(
        sampleFormat srcformat, const AttributesList& attrs) = 0;

    virtual SampleBlockPtr
    DoCreateFromId(sampleFormat srcformat, SampleBlockID id) = 0;
};

#endif
